#pragma once
#include <map>
#include <list>
#include "RCTP.h"

struct sockaddr_in;
struct ip_mreq;

class RUDP
{
 
public:
    RUDP();
    virtual ~RUDP();

    virtual bool Initialize(const char* ip, uint16_t port);

    virtual void Terminate();

    virtual void Update(uint32_t timeout = 0);

	void SetProtocolFrame(uint64_t frame);

	uint64_t GetProtocolFrame();

	void ClearCache();

	virtual void SendTo(uint8_t* buffer, uint32_t len, const char* ip, const uint16_t port, bool reliable);

	virtual void SendTo(uint8_t* buffer, uint32_t len, sockaddr_in* remote, bool reliable);

    static bool CompareAddr(sockaddr_in* addr1, sockaddr_in* addr2);

	void SetOption(int option, uint32_t value);

	void SetOption(int option, float value);

    uint32_t GetTimestamp();

    bool GetLocalIP(char** outIP);

    bool GetLocalPort(int* outPort);

    bool JoinMulticastGroup(const char* group);

    void DorpMulticastGroup();

	static void ConvertSockAddr(sockaddr_in* addr, const char* ip, uint32_t port);

protected:

    virtual void OnRecvMsg(uint8_t* buffer, uint32_t len, bool reliable, UserData userdata);

    void ErrorLog(const char* _Format, ...);

    void SendToImp(uint8_t* buffer, uint32_t len, UserData ud);

private:

	void SendTo(uint8_t* buffer, uint32_t len, sockaddr_in* remote);

    void HandleRecv();

    void OnSendImp(Message* msg, SendState state);

public:

	template <class _Fx, class _Types>
	void RegisterRecvCallback(_Fx&& _Func, _Types&& _User) {
        onRecv = std::bind(_STD forward<_Fx>(_Func), _User, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5);
	}
	template <class _Fx>
	void RegisterRecvCallback(_Fx&& _Func) {
        onRecv = std::bind(_STD forward<_Fx>(_Func), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5);
	}
	void UnregisterRecvCallback() {
        onRecv = nullptr;
	}

	template <class _Fx, class _Types>
	void RegisterSendCallback(_Fx&& _Func, _Types&& _User) {
		onSend = std::bind(_STD forward<_Fx>(_Func), _User, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
	}
	template <class _Fx>
	void RegisterSendCallback(_Fx&& _Func) {
		onSend = std::bind(_STD forward<_Fx>(_Func), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
	}
	void UnregisterSendCallback() {
		onSend = nullptr;
	}

	template <class _Fx, class _Types>
	void RegisterErrorLogCallback(_Fx&& _Func, _Types&& _User) {
		onErrorLog = std::bind(_STD forward<_Fx>(_Func), _User, std::placeholders::_1, std::placeholders::_2);
	}
	template <class _Fx>
	void RegisterErrorLogCallback(_Fx&& _Func) {
		onErrorLog = std::bind(_STD forward<_Fx>(_Func), std::placeholders::_1, std::placeholders::_2);
	}
	void UnregisterErrorLogCallback() {
		onErrorLog = nullptr;
	}

    void* User = nullptr;
protected:
    bool isJoin = false;
private:
    ip_mreq* mreq = nullptr;
    uint64_t udpSocket = 0;
    char buffer[BUFF_SIZE + HEAD_SIZE + 1];
    RCTP* rctp = nullptr;
	uint64_t sendBytes, recvBytes;
private:

    std::function<void(uint8_t* buffer, uint32_t len, sockaddr_in* remote, bool reliable, void* user)> onRecv;
	std::function<void(Message* message, SendState state, void* user)> onSend;
	std::function<void(const char* log, void* user)> onErrorLog;
};

